#include "uart.h"

u8 rxBuffer[RXBUF_SIZE] = {0}; // DMA 接收缓存
uint16_t rxBufferReadPos = 0;  // 接收缓冲区读指针

/**
 * @brief  USART1 GPIO初始化（PD5:TX, PD6:RX）
 */
void UART1_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

    // TX: PD5
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_30MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    // RX: PD6
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    // RE/DE: PC7
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_30MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

    GPIO_ResetBits(GPIOC, GPIO_Pin_7); // 默认接收
}

/**
 * @brief  USART1初始化
 * @param  baudrate: 波特率
 */
void UART1_Init(uint32_t baudrate)
{
    USART_InitTypeDef USART_InitStructure = {0};
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

    USART_InitStructure.USART_BaudRate = baudrate;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
    USART_Init(USART1, &USART_InitStructure);

    DMA_Cmd(DMA1_Channel4, ENABLE); /* USART1 Tx */
    DMA_Cmd(DMA1_Channel5, ENABLE); /* USART1 Rx */

    USART_Cmd(USART1, ENABLE);
}

/**
 * @brief  DMA初始化（通道4:TX, 通道5:RX）
 */
void UART1_DMA_Init(void)
{
    DMA_InitTypeDef DMA_InitStructure = {0};
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    DMA_DeInit(DMA1_Channel4);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART1->DATAR);       // DMA 外设基址，需指向对应的外设
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)rxBuffer;                   // DMA 内存基址，指向发送缓冲区的首地址。未指定合法地址就启动 DMA 会进 hardfault
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;                      // 方向 : 外设 作为 终点，即 内存 ->  外设
    DMA_InitStructure.DMA_BufferSize = 0;                                   // 缓冲区大小,即要 DMA 发送的数据长度,目前没有数据可发
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;        // 外设地址自增，禁用
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                 // 内存地址自增，启用
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据位宽，8位(Byte)
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;         // 内存数据位宽，8位(Byte)
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                           // 普通模式，发完结束，不循环发送
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;                 // 优先级最高
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                            // 实际为 M2P,所以禁用 M2M
    DMA_Init(DMA1_Channel4, &DMA_InitStructure);

    // RX DMA 初始化，环形缓冲区自动接收
    DMA_DeInit(DMA1_Channel5);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART1->DATAR);
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)rxBuffer; // 接收缓冲区
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;    // 方向 : 外设 作为 源，即 内存 <- 外设
    DMA_InitStructure.DMA_BufferSize = RXBUF_SIZE;        // 缓冲区长度为 RXBUF_SIZE
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;       // 循环模式，构成环形缓冲区
    DMA_Init(DMA1_Channel5, &DMA_InitStructure);
}

/*******************************************************************************
 * Function Name  :  uartWriteBlocking
 * Description    :  send data via USARTx           用USARTx发送数据
 * Input          :  char * data          data to send  要发送的数据的首地址
 *                   uint16_t num         number of data    数据长度
 * Return         :  RESET                USARTx busy,failed to send    发送失败
 *                   SET                  send success              发送成功
 *******************************************************************************/
char uartWriteBlocking(char *data, uint16_t num)
{
    // 如果发送完成
    if (DMA_GetCurrDataCounter(DMA1_Channel4) == 0)
    {
        DMA_Cmd(DMA1_Channel4, DISABLE);            // 关 DMA 后操作
        DMA1_Channel4->MADDR = (uint32_t)data;      // 发送缓冲区为 data
        DMA_SetCurrDataCounter(DMA1_Channel4, num); // 设置缓冲区长度
        DMA_Cmd(DMA1_Channel4, ENABLE);             // 开 DMA
        return SET;
    }
    return 0;
}

/*******************************************************************************
 * Function Name  :  uartWriteStrBlocking
 * Description    :  send string via USARTx 用USARTx发送字符串
 * Input          :  char * str                     string to send
 * Return         :  RESET                          USARTx busy,failed to send    发送失败
 *                   SET                            send success              发送成功
 *******************************************************************************/
char uartWriteStrBlocking(char *str)
{
    uint16_t num = 0;
    while (str[num])
        num++; // 计算字符串长度
    return uartWriteBlocking(str, num);
}

/*******************************************************************************
 * Function Name  :  uartRead
 * Description    :  read some bytes from receive buffer 从接收缓冲区读出一组数据
 * Input          :  char * buffer                  buffer to storage the data    用来存放读出数据的地址
 *                   uint16_t num                   number of data to read        要读的字节数
 * Return         :  int                  number of bytes read      返回实际读出的字节数
 *******************************************************************************/
uint32_t uartRead(char *buffer, uint16_t num)
{
    uint16_t rxBufferEnd = RXBUF_SIZE - DMA_GetCurrDataCounter(DMA1_Channel5); // 计算 DMA 数据尾的位置
    uint16_t i = 0;

    if (rxBufferReadPos == rxBufferEnd)
    {
        // 无数据，返回
        return 0;
    }

    while (rxBufferReadPos != rxBufferEnd && i < num)
    {
        buffer[i] = rxBuffer[rxBufferReadPos];
        i++;
        rxBufferReadPos++;
        if (rxBufferReadPos >= RXBUF_SIZE)
        {
            // 读指针超出接收缓冲区，回零
            rxBufferReadPos = 0;
        }
    }
    return i;
}

/*******************************************************************************
 * Function Name  :  uartReadByte
 * Description    :  read one byte from UART buffer 从接收缓冲区读出 1 字节数据
 * Input          :  none
 * Return         :  char    read data              返回读出的数据(无数据也返回0)
 *******************************************************************************/
char uartReadByte()
{
    char ret;
    uint16_t rxBufferEnd = RXBUF_SIZE - DMA_GetCurrDataCounter(DMA1_Channel5); // 计算 DMA 数据尾的位置
    if (rxBufferReadPos == rxBufferEnd)
    {
        // 无数据，返回
        return 0;
    }
    ret = rxBuffer[rxBufferReadPos];
    rxBufferReadPos++;
    if (rxBufferReadPos >= RXBUF_SIZE)
    {
        // 读指针超出接收缓冲区，回零
        rxBufferReadPos = 0;
    }
    return ret;
}

/*******************************************************************************
 * Function Name  :  uartAvailable
 * Description    :  get number of bytes Available to read from the UART buffer 获取缓冲区中可读数据的数量
 * Input          :  none
 * Return         :  uint16_t    number of bytes Available to read              返回可读数据数量
 *******************************************************************************/
uint16_t uartAvailable()
{
    uint16_t rxBufferEnd = RXBUF_SIZE - DMA_GetCurrDataCounter(DMA1_Channel5); // 计算 DMA 数据尾的位置
    // 计算可读字节
    if (rxBufferReadPos <= rxBufferEnd)
    {
        return rxBufferEnd - rxBufferReadPos;
    }
    else
    {
        return rxBufferEnd + RXBUF_SIZE - rxBufferReadPos;
    }
}

/**
 * @brief 读取当前读取位置一定偏移处的数据，不移动读取指针
 *
 * @param offset    : 偏移量
 * @return char     : 读取的数据
 */
inline char uartPeek(uint8_t Serial_num, uint16_t offset)
{
    return rxBuffer[(rxBufferReadPos + offset) % RXBUF_SIZE];
}

void UARTx_DMA_init(uint32_t btl)
{
    UART1_DMA_Init();
    UART1_GPIO_Init(); // 初始化 GPIO
    UART1_Init(btl);
    DMA_Cmd(DMA1_Channel4, ENABLE);  /* USART1 Tx */
    DMA_Cmd(DMA1_Channel5, ENABLE);  /* USART1 Rx */
    USART_DMACmd(USART1, USART_DMAReq_Tx | USART_DMAReq_Rx, ENABLE);
}
